#include "stdafx.h"
#include "LiteDataBase.h"


using namespace std;
using namespace LIBSQLITE4CONFIGSPEACE;

SQLite4MiniDB * SQLite4MiniDB::GetMiniDB(std::string dbname, std::list<std::string> columns)
{
	return CLiteDataBase::GetMiniDB(dbname, columns);
}


CLiteDataBase::CLiteDataBase(std::string filename, std::list<std::string> columns) :CFileManager(filename), mColumns(columns)
{
}


CLiteDataBase::~CLiteDataBase()
{
}

CLiteDataBase * CLiteDataBase::GetMiniDB(std::string filename, std::list<std::string> columns)
{
	InitialGlobalResource();
	CLiteDataBase* obj = new CLiteDataBase(filename, columns);
	if (!obj) {
		LogInfo("Error: New CLiteConfig object failed!");
		return nullptr;
	}
	if (FindAndAddDBFile(filename, reinterpret_cast<uintptr_t>(obj)) && obj->Initialize()) {
		return obj;
	}
	return nullptr;
}

long CLiteDataBase::Insert(std::list<std::string> columns, std::list<std::string> values, bool unique)
{
	if (columns.size() != values.size())
		return -2;

	char *errmsg = nullptr;
	string sqlcmd = "Insert into " + GetTableName() + " (";
	bool firstcolumn = true;
	for each (string  column in columns)
	{
		if (!firstcolumn)
			sqlcmd += ",";
		else
			firstcolumn = false;
		sqlcmd += column;
	}
	sqlcmd += ") VALUES (";
	firstcolumn = true;
	for each (string val in values)
	{
		if (!firstcolumn)
			sqlcmd += ",";
		else
			firstcolumn = false;
		sqlcmd += "\"" + val + "\"";
	}
	sqlcmd += ")";
	if (sqlite3_exec(mSession, sqlcmd.c_str(), Sqlite4MiniDBCB, nullptr, &errmsg)) {
		LogInfo("Insert failed! %s\n", errmsg);
		return -1;
	}

	return 0;
}

long CLiteDataBase::Update(std::list<std::string> columns, std::list<std::string> values, std::string conditions)
{
	if (columns.size() != values.size())
		return -2;

	char *errmsg = nullptr;
	string sqlcmd = "Update " + GetTableName() + " SET ";
	bool firstcolumn = true;

	for (std::list<std::string>::iterator cit = columns.begin(), vit = values.begin(); cit != columns.end(); cit++, vit++) {
		if (!firstcolumn)
			sqlcmd += ",";
		else
			firstcolumn = false;
		sqlcmd += *cit + " = \"" + *vit + "\"";
	}

	if (conditions.size()) {
		sqlcmd += " Where " + conditions;
	}

	if (sqlite3_exec(mSession, sqlcmd.c_str(), Sqlite4MiniDBCB, nullptr, &errmsg)) {
		LogInfo("Update failed! %s\n", errmsg);
		return -1;
	}
	return 0;
}

long CLiteDataBase::Find(std::list<std::string> columns, std::map<std::string, std::list<std::string>>& values, std::string conditions)
{
	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	string sqlcmd = "select count(*) From " + GetTableName();
	if (conditions.length()) {
		sqlcmd += " where " + conditions;
	}
	sqlcmd += string(";") + "select ";
	bool firstcolumn = true;
	if (columns.size() == 0) {
		sqlcmd += "*";
	}
	else {
		for each (string column in columns)
		{
			if (!firstcolumn)
				sqlcmd += ",";
			else
				firstcolumn = false;
			sqlcmd += column;
		}
	}
	sqlcmd += " From " + GetTableName();
	if (conditions.length()) {
		sqlcmd += " where " + conditions;
	}
	if (sqlite3_exec(mSession, sqlcmd.c_str(), Sqlite4MiniDBCB, &args, &errmsg)) {
		LogInfo("Find failed! %s\n", errmsg);
		return -1;
	}

	DWORD dwRS = WaitForSingleObject(args.EventObj, 2000);
	int rc = 0;
	switch (dwRS)
	{
	case WAIT_OBJECT_0://we got some result
		if (args.total == 0) {
			return 0;
		}
		values = args.values;
		break;
	default://we're timeout here
		LogInfo("Find value Timeout\n");
		break;
	}
	return 0;
}

int CLiteDataBase::Remove(std::string conditions)
{
	string sqlcmd = "delete From " + GetTableName();
	if (conditions.length()) {
		sqlcmd += " where " + conditions;
	}
	char *errmsg = nullptr;
	if (sqlite3_exec(mSession, sqlcmd.c_str(), Sqlite4MiniDBCB, NULL, &errmsg)) {
		LogInfo("Delete failed! %s\n", errmsg);
		return -1;
	}
	return 0;
}

int CLiteDataBase::Sqlite4MiniDBCB(void *args, int number, char ** value, char ** name)
{
	SQLiteCBArg* sqliteArgs = reinterpret_cast<SQLiteCBArg*>(args);
	if (sqliteArgs->total < 0) {//first return. this should for count(), because all select result should follow count() cmd, if no row we woun't be here
		sqliteArgs->total = atoi(value[0]);
		if (sqliteArgs->total == 0) {//maybe no result
			SetEvent(reinterpret_cast<SQLiteCBArg*>(args)->EventObj);
			return 0;
		}
		return 0;
	}
	if (sqliteArgs->total == 0) {//maybe timeout
		return 0;
	}

	for (int i = 0; i < number; i++) {
		sqliteArgs->values[name[i]].push_back(value[i]);
	}
	if (sqliteArgs->values.size() == sqliteArgs->total) {//total completed
		SetEvent(reinterpret_cast<SQLiteCBArg*>(args)->EventObj);
		return 0;
	}
	return 0;
}

bool CLiteDataBase::Initialize()
{
	int ret = OpenDB(GetFileName());
	if (ret != 0) {
		LogInfo("CLiteDataBase::Initialize Open DBFile Failed with return code:%u", ret);
		return false;
	}

	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	if (sqlite3_exec(mSession, "SELECT count(name) FROM sqlite_master WHERE type='table';SELECT name FROM sqlite_master WHERE type='table'",
		Sqlite4MiniDBCB, &args, &errmsg)) {
		LogInfo("CLiteConfig::Initialize failed! %s\n", errmsg);
		return false;
	}

	DWORD dwRS = WaitForSingleObject(args.EventObj, 10);
	int rc = 0;

	switch (dwRS)
	{
	case WAIT_OBJECT_0://we got some result
		if (args.total == 0) {//empty db file
			string sqlcmd = "CREATE TABLE " + GetTableName() + " (";//ID INTEGER PRIMARY KEY AUTOINCREMENT ";
			bool firstclomn = true;
			for each (string column in mColumns) {
				if (!firstclomn) {
					sqlcmd += ", ";
				}
				else {
					firstclomn = false;
				}
				sqlcmd += column + " NUMERIC NOT NULL";
			}
			sqlcmd += ")";//Item INTEGER NOT NULL)";
			rc = sqlite3_exec(mSession, sqlcmd.c_str(),
				Sqlite4MiniDBCB, &mSyncObj, &errmsg);
			if (rc) {
				LogInfo("CLiteConfig::Initialize Try to create Table with error:%u: %s\n", rc, errmsg);
			}
			break;
		}
		if (args.total >= 2) {
			LogInfo("Warning: CLiteDataBase should contain so much tables\n");
		}
		for each (string name in args.values.begin()->second)
		{
			if (name != "sqlite_sequence") {//this is not auto create table, we use it
				GetTableName() = name;//if it's multiple result, we use the lastest
			}
		}
		break;
	default://we're timeout here
		LogInfo("CLiteConfig::Initialize Initial DB File Timeout\n");
		break;
	}
	return true;
}
